﻿//
// PluginsForm.cs: Form for managing plugins.
//
// Copyright 2010 Michael Goethe
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using S9PalCommon;

namespace S9Pal
{
    /// <summary>
    /// Form for managing plugins.
    /// </summary>
    public partial class PluginsForm : Form
    {
        #region Fields

        Dictionary<String, bool> localPluginComponentEnabled;

        #endregion

        #region Constructors

        public PluginsForm()
        {
            InitializeComponent();
        }

        #endregion

        #region Private Methods

        private void CheckSubNodes(TreeNode node)
        {
            for (int i = 0; i < node.Nodes.Count; i++)
            {
                node.Nodes[i].Checked = node.Checked;
                CheckSubNodes(node.Nodes[i]);
            }
        }

        private void UpdateTreeViewCheckBoxes(TreeNodeCollection topNodes)
        {
            foreach (TreeNode node in topNodes)
            {
                // As per Form_Load code, only nodes with tags represent a component item, so no need to check with ContainsKey method.
                // Also, we only need to update the leaf nodes, because the AfterCheck event will recursively manage the parent nodes.
                // Update: The plugin itself now has a tag, so let's also check that this isn't an IPlugin.
                if (node.Tag != null && !(node.Tag is IPlugin))
                    node.Checked = localPluginComponentEnabled[(String)node.Tag];

                UpdateTreeViewCheckBoxes(node.Nodes);
            }
        }

        #endregion

        #region Events

        private void PluginsForm_Load(object sender, EventArgs e)
        {
            int i = 0;

            localPluginComponentEnabled = new Dictionary<String, bool>(Global.Settings.PluginComponentEnabled);

            // Tells our TreeView control to halt business as usual (redraw, events, etc.), until we finish updating it.
            pluginTreeView.BeginUpdate();
            foreach (AvailablePlugin plugin in Global.Plugins.AvailablePlugins)
            {
                TreeNode pluginNode = new TreeNode(plugin.Instance.Name);
                pluginNode.Tag = plugin.Instance;

                // Add the top node for the plugin.
                pluginTreeView.Nodes.Add(pluginNode);

                // If we have MainControls, add a 'Main Controls' node.
                if (plugin.Instance.MainControls.Count > 0)
                    pluginTreeView.Nodes[i].Nodes.Add("Main Controls");
                // Populate 'Main Controls' node with the MainControls available within the plugin. Specify (String)Tags representing the control's type.
                foreach (IMainControl imc in plugin.Instance.MainControls)
                {
                    TreeNode node = new TreeNode(imc.DisplayTitle);
                    node.Tag = imc.GetType().ToString();
                    pluginTreeView.Nodes[i].Nodes[0].Nodes.Add(node);
                }

                // If we have Workers, add a 'Workers' node.
                if (plugin.Instance.Workers.Count > 0)
                    pluginTreeView.Nodes[i].Nodes.Add("Workers");
                // Populate 'Workers' node with the Workers available within the plugin. Specify (String)Tags representing the worker's type.
                foreach (Worker worker in plugin.Instance.Workers)
                {
                    TreeNode node = new TreeNode(worker.Name);
                    node.Tag = worker.GetType().ToString();
                    pluginTreeView.Nodes[i].Nodes[1].Nodes.Add(node);
                }

                // If we have Syncers, add a 'Syncers' node.
                if (plugin.Instance.Syncers.Count > 0)
                    pluginTreeView.Nodes[i].Nodes.Add("Syncers");
                // Populate 'Syncers' node with the Syncers available within the plugin. Specify (String)Tags representing the syncer's type.
                foreach (Syncer syncer in plugin.Instance.Syncers)
                {
                    TreeNode node = new TreeNode(syncer.Name);
                    node.Tag = syncer.GetType().ToString();
                    pluginTreeView.Nodes[i].Nodes[2].Nodes.Add(node);
                }

                i++;
            }
            // Let our TreeView continue business as usual.
            pluginTreeView.EndUpdate();

            // Update the Checked values of our nodes.
            UpdateTreeViewCheckBoxes(pluginTreeView.Nodes);
        }

        private void pluginTreeView_AfterCheck(object sender, TreeViewEventArgs e)
        {
            // If the user himself has changed the Checked value, update all the child nodes to reflect the same value as the node he changed.
            // We want to make sure the user is performing these actions to ensure that we don't start working both ways. Further down we have
            // code that manages the parent nodes to reflect the values of their children, so this is important to CHECK for (bad pun ;).
            if (e.Action == TreeViewAction.ByMouse || e.Action == TreeViewAction.ByKeyboard)
                CheckSubNodes(e.Node);

            // If the current node has a parent see if we need to update it to better reflect the values of its children.
            if (e.Node.Parent != null)
            {
                // If the current node is checked, make sure its parent is also checked.
                if (e.Node.Checked)
                {
                    e.Node.Parent.Checked = true;
                }
                // If the current node is not checked, see if its parent has any children that are still checked. If it doesn't, uncheck it.
                else
                {
                    for (int i = 0; i < e.Node.Parent.Nodes.Count; i++)
                    {
                        if (e.Node.Parent.Nodes[i].Checked)
                            break;

                        if (i == e.Node.Parent.Nodes.Count - 1)
                            e.Node.Parent.Checked = false;
                    }
                }
            }

            // Update our local plugin component collection to reflect any component Checked values that might have changed.
            // As per Form_Load code, only nodes with tags represent a component item, so no need to check with ContainsKey method.
            // Update: The plugin itself now has a tag, so let's also check that this isn't an IPlugin.
            if (e.Node.Tag != null && !(e.Node.Tag is IPlugin))
                localPluginComponentEnabled[(String)e.Node.Tag] = e.Node.Checked;
        }

        private void pluginTreeView_AfterSelect(object sender, TreeViewEventArgs e)
        {
            TreeNode node = e.Node;
            IPlugin plugin;

            // Get the root node of the selected tree, which will contain a plugin reference in the Tag object.
            while (node.Parent != null)
                node = node.Parent;

            // Cast the tag and set it to the plugin.
            plugin = (IPlugin)node.Tag;

            // Update the plugin information.
            pluginInfoLabel.Text = plugin.Name + "\n" + plugin.Version + "\n\n" + plugin.Description + "\n\n" + "Written by " + plugin.Author + ".";
        }

        private void cancelButton_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void okButton_Click(object sender, EventArgs e)
        {
            Global.Settings.PluginComponentEnabled = localPluginComponentEnabled;

            (this.Owner as MainForm).PurgePlugins();
            (this.Owner as MainForm).LoadPlugins();

            this.Close();
        }

        #endregion
    }

    internal class DoubleClicklessTreeView : TreeView
    {
        protected override void WndProc(ref Message m)
        {
            // Suppress WM_LBUTTONDBLCLK
            if (m.Msg == 0x203) { m.Result = IntPtr.Zero; }
            else base.WndProc(ref m);
        }
    }
}
